package com.laxcen.facestaticcheck.engine;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.laxcen.facestaticcheck.Const;
import com.laxcen.facestaticcheck.core.DetectionResult;
import com.laxcen.facestaticcheck.core.EngineWrapper;
import com.laxcen.facestaticcheck.util.NV21ToBitmap;
import com.laxcen.facestaticcheck.util.OpenCVUtil;

import org.opencv.android.InstallCallbackInterface;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;


/**
 *  活体检测
 */
public class FaceCheckEngine {
    private static FaceCheckEngine faceCheckEngine;
    private EngineWrapper engineWrapper;
    public static FaceCheckEngine getInstance(Context context){
        if(faceCheckEngine==null){
            synchronized (FaceCheckEngine.class){
                faceCheckEngine = new FaceCheckEngine();
                faceCheckEngine.nv21ToBitmap = new NV21ToBitmap(context);
                faceCheckEngine.engineWrapper = new EngineWrapper(context.getAssets());
                faceCheckEngine.engineWrapper.init();
                //载入OpenCV
                OpenCVLoader.initDebug(false);
                OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, context, new LoaderCallbackInterface() {
                    @Override
                    public void onManagerConnected(int status) {

                    }

                    @Override
                    public void onPackageInstall(int operation, InstallCallbackInterface callback) {

                    }
                });
            }
        }
        return faceCheckEngine;
    }

    private void sendAction(int action){
        sendAction(action,null);
    }
    private void sendAction(int action,Bitmap bitmap){
        if(!isStart){
            return;
        }
        Message message = new Message();
        message.what = action;
        if(bitmap!=null){
            message.obj = bitmap;
        }
        handler.sendMessage(message);
    }

    private long startTime = 0l;
    private long waitTime = 0l;
    private boolean hasStop = false;
    private int orientation = 7;
    private byte[] mNV21Bytes;
    private int width,height;
    public void setWidthAndHeight(int width,int height){
        this.width = width;
        this.height = height;
        Log.e("setWidthAndHeight",width+"/"+height);
    }
    public void onLiveFaceCheck(byte[] mNV21Bytes,int previewWidth,int previewHeight,boolean isShow){
        if(mNV21Bytes==null){
            return;
        }
        DetectionResult detectionResult = engineWrapper.detect(mNV21Bytes,previewWidth,previewHeight,orientation);
        if (isShow&&hasStop) {
            onStartCheck();
        }
        detectionResult.setWidth(previewWidth);
        detectionResult.setHeight(previewHeight);
        if(hasStop){
            return;
        }
        //判断是否进入取景框区域
        boolean hasIntoFaceArea = false;
        //屏幕中心点
        int halfHeight = detectionResult.getWidth()/2;
        int halfWidth = detectionResult.getHeight()/2;
        //头像区域宽
        int headHeight = detectionResult.getRight()-detectionResult.getLeft();
        //头像区域高
        int headWidth = detectionResult.getBottom()-detectionResult.getTop();
        //头像中心位置
        int centerPointY = detectionResult.getRight() - headWidth/2;
        int centerPointX = detectionResult.getBottom() - headHeight/2;
        //判断人脸是否在中心区域
        if(centerPointY>halfWidth-width/2
                &&centerPointY<halfWidth+width/2
                &&centerPointX>halfHeight-height/2
                &&centerPointX<halfHeight+height/2){
            hasIntoFaceArea = true;
        }
        if(onLogListener!=null){
            String log = "halfWidth="+halfWidth+",halfHeight="+halfHeight+"\n";
            log+="headWidth="+headWidth+",headHeight="+headHeight+"\n";
            log+="centerPointX="+centerPointX+",centerPointY="+centerPointY+"\n";
            log+="width="+width+",height="+height+"\n";
            log+="人脸是否进入取景框："+hasIntoFaceArea
                    +"\n"+"人脸中心位置:["+centerPointY+","+centerPointX+"]"
                    +"\n"+"取景框中心位置识别区:X["+(halfWidth-width/2)+","+(halfWidth+width/2)+"],Y["+(halfHeight-height/2)+","+(halfHeight+height/2)+"]";
            onLogListener.onLog(0,log);
        }
        if(hasIntoFaceArea&&detectionResult.getHasFace()){
            //计算头像与镜头的距离
            float areaRatio =(headWidth*headHeight)/((float)detectionResult.getWidth()*detectionResult.getHeight())*100;
            if(onLogListener!=null){
                onLogListener.onLog(1,"与镜头的距离："+areaRatio+",距离范围:["+Const.FACE_MAX_FAR+" - "+Const.FACE_MIN_CLOSE+"]");
            }
            //太远
            if(areaRatio<=Const.FACE_MAX_FAR){
                startTime = 0l;
                sendAction(Const.ACTION_TYPE_CAMERA_TOO_FAR );
                return;
            //太近
            }else if(areaRatio>=Const.FACE_MIN_CLOSE){
                startTime = 0l;
                sendAction(Const.ACTION_TYPE_CAMERA_TOO_CLOSE );
                return;
            }

            if(onLogListener!=null){
                onLogListener.onLog(2,"活体可信度："+detectionResult.getConfidence()+",可信度阀值:"+Const.FACE_CONFIDENCE);
            }

            //识别成功
            if(detectionResult.getConfidence()>=Const.FACE_CONFIDENCE){
                this.mNV21Bytes = mNV21Bytes;
                //去拿图片
                toGetImage(detectionResult);
            }else{
                //识别假脸处理（通过图片、视频、打印纸质等）
                String fakeFaceStr = String.valueOf(detectionResult.getConfidence());
                if(fakeFaceStr.contains(".")){
                    int fakeFace = Integer.parseInt(fakeFaceStr.substring(0,fakeFaceStr.indexOf(".")));
                    if(fakeFace>0){
                        if(onLogListener!=null){
                            onLogListener.onLog(2,"活体可信度：-"+fakeFace+",可信度阀值:"+Const.FACE_CONFIDENCE);
                        }
                        sendAction(Const.ACTION_TYPE_NOT_LIVE_FACE );
                        return;
                    }
                }
                if(startTime==0l){
                    startTime = System.currentTimeMillis();
                }
                if(System.currentTimeMillis()-startTime>Const.CHECK_TIME_OUT*1000){
                    //识别超时
                    sendAction(Const.ACTION_TYPE_TIME_OUT );
                    return;
                }else{
                    //识别中
                    sendAction(Const.ACTION_TYPE_CHECKING);
                    if(onLogListener!=null){
                        onLogListener.onLog(3,"识别超时时间："+(System.currentTimeMillis()-startTime)+"ms");
                    }
                }
            }
        }else{
            if(waitTime==0l){
                waitTime = System.currentTimeMillis();
            }
            if(System.currentTimeMillis()-waitTime>Const.CHECK_WAIT_TIME_OUT*1000){
                //识别超时
                sendAction(Const.ACTION_TYPE_WAIT_TIME_OUT);
                return;
            }else{
                //计时重新算
                startTime = 0l;
                sendAction(Const.ACTION_TYPE_NO_FACE );
                if(onLogListener!=null){
                    onLogListener.onLog(4,"等待检测超时时间："+(System.currentTimeMillis()-waitTime)+"ms");
                }
            }
        }
    }

    //开始检测
    public synchronized void onStartCheck(){
        synchronized (FaceCheckEngine.class){
            hasStop = false;
            bitmap = null;
        }
    }
    //停止检测
    public synchronized void onStopCheck(){
        synchronized (FaceCheckEngine.class){
            startTime = 0l;
            waitTime = 0l;
            hasStop = true;
        }
    }


    /**
     *  回调处理
     */
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int type = msg.what;
            if(onFaceCheckResult!=null){
                Bitmap bitmap = null;
                if(msg.obj!=null){
                    bitmap = (Bitmap) msg.obj;
                }
                onFaceCheckResult.onAction(type,bitmap);
            }
        }
    };
    private OnFaceCheckResult onFaceCheckResult;

    public OnFaceCheckResult getOnFaceCheckResult() {
        return onFaceCheckResult;
    }

    public void setOnFaceCheckResult(OnFaceCheckResult onFaceCheckResult) {
        this.onFaceCheckResult = onFaceCheckResult;
    }

    public void destory() {
        isStart = false;
        faceCheckEngine = null;
        if(engineWrapper!=null){
            engineWrapper.destroy();
        }
    }

    //回调 检查是否有执行该动作
    public interface OnFaceCheckResult{
        //是否执行 执行类型
        void onAction(int type,Bitmap bitmap);
    }


    /**
     * 获取图片部分
     */
    private boolean isStart = true;
    private Thread thread;
    public NV21ToBitmap nv21ToBitmap;
    private void toGetImage(DetectionResult detectionResult){
        if(bitmap!=null){
            return;
        }
        if(!isStart){
            return;
        }
        isStart = false;
        thread = new Thread(() -> {
            try {
                Bitmap bitmap =  nv21ToBitmap.nv21ToBitmap(mNV21Bytes,detectionResult);
               //通过OpenCV 计算图片清晰度
                OpenCVUtil.Result result = OpenCVUtil.detect(bitmap);
                //Log.i("OpenCVUtil",result.getResult()+"/"+result.getThreshold()+"/"+result.isHasError());
                if(onLogListener!=null){
                    onLogListener.onLog(5,"图像清晰度："+result.getResult()+",清晰度阀值:"+Const.FACE_IMAGE_DEFINITION);
                }
                Message message = new Message();
                if(result.isHasError()){
                    message.what=0;
                }else{
                    message.what=1;
                    message.obj = bitmap;
                }
                getBitmapHandler.sendMessage(message);
            }catch (Exception e){
                e.printStackTrace();
            }
            isStart = true;
        });
        thread.start();
    }

    private Bitmap bitmap;
    private Handler getBitmapHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            super.handleMessage(message);
            switch (message.what) {
                case 0:
                    sendAction(Const.ACTION_TYPE_IMAGE_TOO_BLURRED);
                    break;
                case 1:
                    bitmap =  (Bitmap) message.obj;
                    sendAction(Const.ACTION_TYPE_LIVE_FACE ,bitmap);
                    break;
            }
        }
    };


    private OnLogListener onLogListener;

    public OnLogListener getOnLogListener() {
        return onLogListener;
    }

    public void setOnLogListener(OnLogListener onLogListener) {
        this.onLogListener = onLogListener;
    }

    public interface OnLogListener{
        void onLog(int type,String log);
    }
}
